{
 "cells": [
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "基于用户的协同过滤"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "import sys\n",
    "import imp\n",
    "imp.reload(sys)\n",
    "\n",
    "import pandas as pd\n",
    "import numpy as np\n",
    "\n",
    "import pickle\n",
    "import scipy.io as sio\n",
    "import os\n",
    "import scipy.spatial.distance as ssd"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "初始化"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "user_index = pickle.load(open(\"user_index.pkl\",'rb'))\n",
    "item_index = pickle.load(open(\"item_index.pkl\",'rb'))\n",
    "\n",
    "n_users = len(user_index)\n",
    "n_items = len(item_index)\n",
    "\n",
    "user_item_scores = sio.mmread(\"user_items_scores\").todense()\n",
    "\n",
    "user_items = pickle.load(open(\"user_items.pkl\",'rb'))\n",
    "item_users = pickle.load(open(\"item_users.pkl\",'rb'))\n",
    "#所有item之间的相似度\n",
    "similarity_matrix = pickle.load(open(\"items_similarity.pkl\",'rb'))\n"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "计算平均打分"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "mu = np.zeros(n_users)\n",
    "for u in range(n_users):\n",
    "    n_items_user = 0\n",
    "    r_acc = 0.0\n",
    "    \n",
    "    for i in user_items[u]:\n",
    "        r_acc += user_item_scores[u,i]\n",
    "        n_items_user += 1 \n",
    "        \n",
    "    mu[u] = r_acc/n_items_user"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([0.00470798, 0.01424275, 0.00961421, ..., 0.08912656, 0.00853275,\n",
       "       0.01698842])"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "mu"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "计算两个用户的相似度"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "uid1 = 0\n",
    "uid2 = 1\n",
    "si = {}\n",
    "for item in user_items[uid1]:\n",
    "    if item in user_items[uid2]:\n",
    "        si[item] = 1\n",
    "        \n",
    "n = len(si)\n",
    "if (n!=0):\n",
    "    s1 = np.array([user_item_scores[uid1,item] for item in si])\n",
    "    s1 -= mu[uid1]\n",
    "    \n",
    "    s2 = np.array([user_item_scores[uid2,item] for item in si])\n",
    "    s2 -= mu[uid2]\n",
    "    \n",
    "    similarity = 1 - ssd.cosine(s1,s2)\n",
    "else:\n",
    "    similarity = 0.0;"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "def user_similarity(uid1,uid2):\n",
    "    si = {}\n",
    "    for item in user_items[uid1]:\n",
    "        if item in user_items[uid2]:\n",
    "            si[item] = 1\n",
    "            \n",
    "    n = len(si)\n",
    "    if(n==0):\n",
    "        similarity = 0\n",
    "        return similarity\n",
    "    \n",
    "    s1 = np.array([user_item_scores[uid1,item] for item in si])\n",
    "    \n",
    "    s2 = np.array([user_item_scores[uid2,item] for item in si])\n",
    "    \n",
    "    similarity = 1 - ssd.cosine(s1,s2)\n",
    "    return similarity\n"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "预测打分"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "cur_user = '5a905f000fc1ff3df7ca807d57edb608863db05d'\n",
    "cur_user_id = user_index[cur_user]\n",
    "cur_user_items = user_items[cur_user_id]\n",
    "\n",
    "sim_accumulate = 0.0\n",
    "rat_acc = 0.0\n",
    "\n",
    "user_items_scores = np.zeros(n_items)\n",
    "\n",
    "for i in range(n_items):\n",
    "    if i not in cur_user_items:\n",
    "        for user in item_users[i]:\n",
    "            sim = user_similarity(uid1 = user,uid2 = cur_user_id)\n",
    "            \n",
    "            if sim != 0:\n",
    "                rat_acc += sim * (user_item_scores[user,i] - mu[user])\n",
    "                sim_accumulate += sim\n",
    "        \n",
    "        if sim_accumulate != 0:\n",
    "            user_items_scores[i] = rat_acc/sim_accumulate"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "进行推荐"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "sort_index = sorted(((e,i) for i,e in enumerate(list(user_items_scores))),reverse = True)\n",
    "\n",
    "columns = ['user_id','item','score','rank']\n",
    "\n",
    "df = pd.DataFrame(columns = columns)\n",
    "\n",
    "rank = 1\n",
    "for i in range(0,len(sort_index)):\n",
    "    cur_item_index = sort_index[i][1]\n",
    "    cur_item = list(item_index.keys())[list (item_index.values()).index(cur_item_index)]\n",
    "    \n",
    "    if ~np.isnan(sort_index[i][0]) and cur_item_index not in cur_user_items and rank <= 20:\n",
    "        df.loc[len(df)] = [cur_user,cur_item,sort_index[i][0],rank]\n",
    "        rank = rank + 1\n",
    "        "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>user_id</th>\n",
       "      <th>item</th>\n",
       "      <th>score</th>\n",
       "      <th>rank</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>5a905f000fc1ff3df7ca807d57edb608863db05d</td>\n",
       "      <td>SOBONKR12A58A7A7E0</td>\n",
       "      <td>0.015286</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>5a905f000fc1ff3df7ca807d57edb608863db05d</td>\n",
       "      <td>SOAUWYT12A81C206F1</td>\n",
       "      <td>0.011241</td>\n",
       "      <td>2</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>5a905f000fc1ff3df7ca807d57edb608863db05d</td>\n",
       "      <td>SOSXLTC12AF72A7F54</td>\n",
       "      <td>0.009074</td>\n",
       "      <td>3</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>5a905f000fc1ff3df7ca807d57edb608863db05d</td>\n",
       "      <td>SOEGIYH12A6D4FC0E3</td>\n",
       "      <td>0.007092</td>\n",
       "      <td>4</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>5a905f000fc1ff3df7ca807d57edb608863db05d</td>\n",
       "      <td>SOFRQTD12A81C233C0</td>\n",
       "      <td>0.006822</td>\n",
       "      <td>5</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>5a905f000fc1ff3df7ca807d57edb608863db05d</td>\n",
       "      <td>SOAXGDH12A8C13F8A1</td>\n",
       "      <td>0.006078</td>\n",
       "      <td>6</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>6</th>\n",
       "      <td>5a905f000fc1ff3df7ca807d57edb608863db05d</td>\n",
       "      <td>SOUFTBI12AB0183F65</td>\n",
       "      <td>0.005908</td>\n",
       "      <td>7</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7</th>\n",
       "      <td>5a905f000fc1ff3df7ca807d57edb608863db05d</td>\n",
       "      <td>SOVDSJC12A58A7A271</td>\n",
       "      <td>0.005869</td>\n",
       "      <td>8</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>8</th>\n",
       "      <td>5a905f000fc1ff3df7ca807d57edb608863db05d</td>\n",
       "      <td>SOPUCYA12A8C13A694</td>\n",
       "      <td>0.005793</td>\n",
       "      <td>9</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>9</th>\n",
       "      <td>5a905f000fc1ff3df7ca807d57edb608863db05d</td>\n",
       "      <td>SOOFYTN12A6D4F9B35</td>\n",
       "      <td>0.005721</td>\n",
       "      <td>10</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>10</th>\n",
       "      <td>5a905f000fc1ff3df7ca807d57edb608863db05d</td>\n",
       "      <td>SOHTKMO12AB01843B0</td>\n",
       "      <td>0.005641</td>\n",
       "      <td>11</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>11</th>\n",
       "      <td>5a905f000fc1ff3df7ca807d57edb608863db05d</td>\n",
       "      <td>SOBOUPA12A6D4F81F1</td>\n",
       "      <td>0.005542</td>\n",
       "      <td>12</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>12</th>\n",
       "      <td>5a905f000fc1ff3df7ca807d57edb608863db05d</td>\n",
       "      <td>SONYKOW12AB01849C9</td>\n",
       "      <td>0.005471</td>\n",
       "      <td>13</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>13</th>\n",
       "      <td>5a905f000fc1ff3df7ca807d57edb608863db05d</td>\n",
       "      <td>SODJWHY12A8C142CCE</td>\n",
       "      <td>0.005039</td>\n",
       "      <td>14</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>14</th>\n",
       "      <td>5a905f000fc1ff3df7ca807d57edb608863db05d</td>\n",
       "      <td>SOLFXKT12AB017E3E0</td>\n",
       "      <td>0.004616</td>\n",
       "      <td>15</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>15</th>\n",
       "      <td>5a905f000fc1ff3df7ca807d57edb608863db05d</td>\n",
       "      <td>SOFLJQZ12A6D4FADA6</td>\n",
       "      <td>0.004217</td>\n",
       "      <td>16</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>16</th>\n",
       "      <td>5a905f000fc1ff3df7ca807d57edb608863db05d</td>\n",
       "      <td>SOTWNDJ12A8C143984</td>\n",
       "      <td>0.004104</td>\n",
       "      <td>17</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>17</th>\n",
       "      <td>5a905f000fc1ff3df7ca807d57edb608863db05d</td>\n",
       "      <td>SOUNZHU12A8AE47481</td>\n",
       "      <td>0.004086</td>\n",
       "      <td>18</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>18</th>\n",
       "      <td>5a905f000fc1ff3df7ca807d57edb608863db05d</td>\n",
       "      <td>SOUVTSM12AC468F6A7</td>\n",
       "      <td>0.003798</td>\n",
       "      <td>19</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>19</th>\n",
       "      <td>5a905f000fc1ff3df7ca807d57edb608863db05d</td>\n",
       "      <td>SOUDLVN12AAFF43658</td>\n",
       "      <td>0.003615</td>\n",
       "      <td>20</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                                     user_id                item     score  \\\n",
       "0   5a905f000fc1ff3df7ca807d57edb608863db05d  SOBONKR12A58A7A7E0  0.015286   \n",
       "1   5a905f000fc1ff3df7ca807d57edb608863db05d  SOAUWYT12A81C206F1  0.011241   \n",
       "2   5a905f000fc1ff3df7ca807d57edb608863db05d  SOSXLTC12AF72A7F54  0.009074   \n",
       "3   5a905f000fc1ff3df7ca807d57edb608863db05d  SOEGIYH12A6D4FC0E3  0.007092   \n",
       "4   5a905f000fc1ff3df7ca807d57edb608863db05d  SOFRQTD12A81C233C0  0.006822   \n",
       "5   5a905f000fc1ff3df7ca807d57edb608863db05d  SOAXGDH12A8C13F8A1  0.006078   \n",
       "6   5a905f000fc1ff3df7ca807d57edb608863db05d  SOUFTBI12AB0183F65  0.005908   \n",
       "7   5a905f000fc1ff3df7ca807d57edb608863db05d  SOVDSJC12A58A7A271  0.005869   \n",
       "8   5a905f000fc1ff3df7ca807d57edb608863db05d  SOPUCYA12A8C13A694  0.005793   \n",
       "9   5a905f000fc1ff3df7ca807d57edb608863db05d  SOOFYTN12A6D4F9B35  0.005721   \n",
       "10  5a905f000fc1ff3df7ca807d57edb608863db05d  SOHTKMO12AB01843B0  0.005641   \n",
       "11  5a905f000fc1ff3df7ca807d57edb608863db05d  SOBOUPA12A6D4F81F1  0.005542   \n",
       "12  5a905f000fc1ff3df7ca807d57edb608863db05d  SONYKOW12AB01849C9  0.005471   \n",
       "13  5a905f000fc1ff3df7ca807d57edb608863db05d  SODJWHY12A8C142CCE  0.005039   \n",
       "14  5a905f000fc1ff3df7ca807d57edb608863db05d  SOLFXKT12AB017E3E0  0.004616   \n",
       "15  5a905f000fc1ff3df7ca807d57edb608863db05d  SOFLJQZ12A6D4FADA6  0.004217   \n",
       "16  5a905f000fc1ff3df7ca807d57edb608863db05d  SOTWNDJ12A8C143984  0.004104   \n",
       "17  5a905f000fc1ff3df7ca807d57edb608863db05d  SOUNZHU12A8AE47481  0.004086   \n",
       "18  5a905f000fc1ff3df7ca807d57edb608863db05d  SOUVTSM12AC468F6A7  0.003798   \n",
       "19  5a905f000fc1ff3df7ca807d57edb608863db05d  SOUDLVN12AAFF43658  0.003615   \n",
       "\n",
       "   rank  \n",
       "0     1  \n",
       "1     2  \n",
       "2     3  \n",
       "3     4  \n",
       "4     5  \n",
       "5     6  \n",
       "6     7  \n",
       "7     8  \n",
       "8     9  \n",
       "9    10  \n",
       "10   11  \n",
       "11   12  \n",
       "12   13  \n",
       "13   14  \n",
       "14   15  \n",
       "15   16  \n",
       "16   17  \n",
       "17   18  \n",
       "18   19  \n",
       "19   20  "
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
